namespace SimpleWebServices.Data
{
    using System;
    using System.Net;
    using System.Web;
    using SimpleWebServices;
    using SimpleWebServices.Security.Cryptography;
    using SimpleWebServices.Web;
    using Xunit;

    public sealed class RecordFacts
    {
        [Fact]
        public void typedef()
        {
            Assert.True(new ObjectExpectations<Record>()
                .DerivesFrom<ValueObject<Record>>()
                .IsAbstractBaseClass()
                .Implements<IRecord>()
                .Result);
        }

        [Fact]
        public void ctor()
        {
            Assert.NotNull(new TestRecord() as Record);
        }

        [Fact]
        public void ctor_HttpCacheability()
        {
            Assert.NotNull(new TestRecord(HttpCacheability.Private) as Record);
        }

        [Fact]
        public void prop_Cacheability()
        {
            Assert.True(new PropertyExpectations(typeof(TestRecord).GetProperty("Cacheability"))
                .XmlIgnore()
                .TypeIs<HttpCacheability?>()
                .DefaultValueIs(HttpCacheability.Server)
                .Result);
        }

        [Fact]
        public void prop_Cacheability_get()
        {
            HttpCacheability expected = HttpCacheability.Private;
            HttpCacheability actual = (new TestRecord(expected) as Record).Cacheability.Value;

            Assert.Equal<HttpCacheability>(expected, actual);
        }

        [Fact]
        public void prop_Created()
        {
            Assert.True(new PropertyExpectations(typeof(TestRecord).GetProperty("Created"))
                .XmlIgnore()
                .IsAutoProperty<DateTime?>()
                .Result);
        }

        [Fact]
        public void prop_Entity()
        {
            Assert.True(new PropertyExpectations(typeof(TestRecord).GetProperty("Entity"))
                .TypeIs<string>()
                .Result);
        }

        [Fact]
        public void prop_Etag()
        {
            Assert.True(new PropertyExpectations(typeof(TestRecord).GetProperty("Etag"))
                .XmlIgnore()
                .IsAutoProperty<string>()
                .Result);
        }

        [Fact]
        public void prop_Expires()
        {
            Assert.True(new PropertyExpectations(typeof(TestRecord).GetProperty("Expires"))
                .XmlIgnore()
                .IsAutoProperty<Expiration>()
                .Result);
        }

        [Fact]
        public void prop_Modified()
        {
            Assert.True(new PropertyExpectations(typeof(TestRecord).GetProperty("Modified"))
                .XmlIgnore()
                .IsAutoProperty<DateTime?>()
                .Result);
        }

        [Fact]
        public void prop_Status()
        {
            Assert.True(new PropertyExpectations(typeof(TestRecord).GetProperty("Status"))
                .XmlIgnore()
                .TypeIs<HttpStatusCode?>()
                .DefaultValueIs(HttpStatusCode.OK)
                .Result);
        }

        [Fact]
        public void prop_Token()
        {
            Assert.True(new PropertyExpectations(typeof(TestRecord).GetProperty("Token"))
                .XmlIgnore()
                .IsAutoProperty<Token?>()
                .Result);
        }

        [Fact]
        public void prop_Urn()
        {
            Assert.True(new PropertyExpectations(typeof(TestRecord).GetProperty("Urn"))
                .XmlIgnore()
                .IsAutoProperty<AbsoluteUri>()
                .Result);
        }

        [Fact]
        public void op_RemoveOfT_AbsoluteUri_whenInRepository()
        {
            FakeRepository.Clear();

            try
            {
                AbsoluteUri urn = "urn://test/" + Token.New();

                FakeRepository.Register<TestRecord>(new
                {
                    urn = urn,
                    xml = "<test value='Example' />"
                });

                Assert.True(Record.Exists<TestRecord>(urn));
                Assert.True(Record.Remove<TestRecord>(urn));
                Assert.False(Record.Exists<TestRecord>(urn));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_RemoveOfT_AbsoluteUri_whenNotInRepository()
        {
            FakeRepository.Clear();

            try
            {
                AbsoluteUri urn = "urn://test/" + Token.New();

                FakeRepository.Register<TestRecord>();

                Assert.False(Record.Remove<TestRecord>(urn));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_RemoveOfT_Token_whenInRepository()
        {
            FakeRepository.Clear();

            try
            {
                Token token = Token.New();

                FakeRepository.Register<TestRecord>(new
                {
                    token = token,
                    xml = "<test value='Example' />"
                });

                Assert.True(Record.Exists<TestRecord>(token));
                Assert.True(Record.Remove<TestRecord>(token));
                Assert.False(Record.Exists<TestRecord>(token));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_RemoveOfT_Token_whenNotInRepository()
        {
            FakeRepository.Clear();

            try
            {
                Token token = Token.New();

                FakeRepository.Register<TestRecord>();

                Assert.False(Record.Remove<TestRecord>(token));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_ExistsOfT_AbsoluteUri_whenInRepository()
        {
            FakeRepository.Clear();

            try
            {
                AbsoluteUri urn = "urn://test/" + Token.New();

                FakeRepository.Register<TestRecord>(new
                {
                    urn = urn,
                    xml = "<test value='Example' />"
                });

                Assert.True(Record.Exists<TestRecord>(urn));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_ExistsOfT_AbsoluteUri_whenNotInRepository()
        {
            FakeRepository.Clear();

            try
            {
                AbsoluteUri urn = "urn://test/" + Token.New();

                FakeRepository.Register<TestRecord>();

                Assert.False(Record.Exists<TestRecord>(urn));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_ExistsOfT_string_whenInRepository()
        {
            FakeRepository.Clear();

            try
            {
                FakeRepository.Register<TestRecord>(new
                {
                    token = Token.New(),
                    xml = "<test value='Example' />"
                });

                Assert.True(Record.Exists<TestRecord>("test[@value=\"Example\"]"));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_ExistsOfT_string_whenNotInRepository()
        {
            FakeRepository.Clear();

            try
            {
                FakeRepository.Register<TestRecord>();

                Assert.False(Record.Exists<TestRecord>("test[@value=\"Example\"]"));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_ExistsOfT_Token_whenInRepository()
        {
            FakeRepository.Clear();

            try
            {
                Token token = Token.New();

                FakeRepository.Register<TestRecord>(new
                {
                    token = token,
                    xml = "<test value='Example' />"
                });

                Assert.True(Record.Exists<TestRecord>(token));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_ExistsOfT_Token_whenNotInRepository()
        {
            FakeRepository.Clear();

            try
            {
                FakeRepository.Register<TestRecord>();

                Assert.False(Record.Exists<TestRecord>(Token.New()));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_RetrieveOfT_AbsoluteUri_whenInRepository()
        {
            FakeRepository.Clear();

            try
            {
                AbsoluteUri urn = "urn://test/" + Token.New();

                FakeRepository.Register<TestRecord>(new
                {
                    urn = urn,
                    xml = "<test value='Example' />"
                });

                TestRecord obj = Record.Retrieve<TestRecord>(urn);

                Assert.Equal<string>("Example", obj.Value);
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_RetrieveOfT_AbsoluteUri_whenNotInRepository()
        {
            FakeRepository.Clear();

            try
            {
                AbsoluteUri urn = "urn://test/" + Token.New();

                FakeRepository.Register<TestRecord>();

                Assert.Null(Record.Retrieve<TestRecord>(urn));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_RetrieveOfT_Token_whenInRepository()
        {
            FakeRepository.Clear();

            try
            {
                Token token = Token.New();

                FakeRepository.Register<TestRecord>(new
                {
                    token = token,
                    xml = "<test value='Example' />"
                });

                TestRecord obj = Record.Retrieve<TestRecord>(token);

                Assert.Equal<string>("Example", obj.Value);
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_RetrieveOfT_Token_whenNotInRepository()
        {
            FakeRepository.Clear();

            try
            {
                FakeRepository.Register<TestRecord>();

                Assert.Null(Record.Retrieve<TestRecord>(Token.New()));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_SearchOfT_string_whenInRepository()
        {
            FakeRepository.Clear();

            try
            {
                FakeRepository.Register<TestRecord>(new
                {
                    token = Token.New(),
                    xml = "<test value='Example' />"
                });

                Assert.NotEmpty(Record.Search<TestRecord>("test[@value=\"Example\"]"));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_SearchOfT_string_whenNotInRepository()
        {
            FakeRepository.Clear();

            try
            {
                FakeRepository.Register<TestRecord>();

                Assert.Empty(Record.Search<TestRecord>("test[@value=\"Example\"]"));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_StoreOfT_whenInRepository()
        {
            FakeRepository.Clear();

            try
            {
                DateTimeFactory.Mock = DateTime.UtcNow;

                TestRecord obj = new TestRecord
                {
                    Token = Token.New(),
                    Urn = "urn://test/" + Token.New(),
                    Value = "Example"
                };

                FakeRepository records = FakeRepository.Register<TestRecord>(new
                {
                    token = obj.Token,
                    urn = obj.Urn,
                    created = DateTimeFactory.UtcNow.AddDays(-2),
                    modified = DateTimeFactory.UtcNow.AddDays(-1),
                    status = (int)HttpStatusCode.OK,
                    cacheability = (int)HttpCacheability.Server,
                    etag = MD5Hash.Compute(string.Empty).ToString(),
                    xml = "<test />"
                });

                obj.Store<TestRecord>();

                string xpath =
                    "1=count(" +
                        "/repository" +
                        "/object" +
                            "[@token]" +
                            "[@urn]" +
                            "[@created='" + DateTimeFactory.UtcNow.AddDays(-2).ToXmlString() + "']" +
                            "[@modified='" + DateTimeFactory.UtcNow.ToXmlString() + "']" +
                            "[@status='" + (int)HttpStatusCode.OK + "']" +
                            "[@cacheability='" + (int)HttpCacheability.Server + "']" +
                            "[@etag='" + MD5Hash.Compute(obj.Entity) + "']" +
                        "/test" +
                            "[@value='" + obj.Value + "']" +
                    ")";

                Assert.True((bool)records.Xml.CreateNavigator().Evaluate(xpath));
            }
            finally
            {
                DateTimeFactory.Reset();
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_StoreOfT_whenNotInRepository()
        {
            FakeRepository.Clear();

            try
            {
                DateTimeFactory.Mock = DateTime.UtcNow;

                FakeRepository records = FakeRepository.Register<TestRecord>();

                TestRecord obj = new TestRecord
                {
                    Urn = "urn://test/" + Token.New(),
                    Value = "Example"
                };

                obj.Store<TestRecord>();

                string xpath =
                    "1=count(" +
                        "/repository" +
                        "/object" +
                            "[@token]" +
                            "[@urn]" +
                            "[@created='" + DateTimeFactory.UtcNow.ToXmlString() + "']" +
                            "[@modified='" + DateTimeFactory.UtcNow.ToXmlString() + "']" +
                            "[@status='" + (int)HttpStatusCode.OK + "']" +
                            "[@cacheability='" + (int)HttpCacheability.Server + "']" +
                            "[@etag='" + MD5Hash.Compute(obj.Entity) + "']" +
                        "/test" +
                            "[@value='" + obj.Value + "']" +
                    ")";

                Assert.True((bool)records.Xml.CreateNavigator().Evaluate(xpath));
            }
            finally
            {
                DateTimeFactory.Reset();
                FakeRepository.Reset();
            }
        }
    }
}